package com.oazon.moco.lan.core.http;


import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;

import android.util.Log;

import com.oazon.moco.lan.HttpJsonServerHandler;
import com.oazon.moco.lan.core.http.bean.JsonRequest;
import com.oazon.moco.lan.core.http.bean.JsonResponse;

public class HttpJsonServer {

	private static String TAG = "HttpJsonServer";
	private static HttpJsonServer instance = null;
	private static List<ChannelHandlerContext> clients = new ArrayList<ChannelHandlerContext>();
	
	private Class<? extends ChannelHandler> handlerClass;
	private ChannelFuture future = null;
	private EventLoopGroup bossGroup = null;
	private EventLoopGroup workerGroup = null;
	
	private HttpJsonServerHandler httpJsonServerHandle = null;
	
	private HttpJsonServer(){
		httpJsonServerHandle = new HttpJsonServerHandler(this);
	}
	
	public void start(final int port) throws Exception{
		bossGroup = new NioEventLoopGroup();
		workerGroup = new NioEventLoopGroup();
		try {
			ServerBootstrap b = new ServerBootstrap();
			b.group(bossGroup,workerGroup)
				.channel(NioServerSocketChannel.class)
				.childHandler(new ChannelInitializer<SocketChannel>() {
					@Override
					protected void initChannel(SocketChannel ch) throws Exception {
						ch.pipeline().addLast("http-decoder",new HttpRequestDecoder());
						ch.pipeline().addLast("http-aggregator",new HttpObjectAggregator(65536));
						ch.pipeline().addLast("json-decoder",new HttpJsonRequestDecoder(JsonRequest.class));
						ch.pipeline().addLast("http-encoder",new HttpResponseEncoder());
						ch.pipeline().addLast("json-encoder",new HttpJsonResponseEncoder());
						ch.pipeline().addLast("jsonServerHandler",httpJsonServerHandle);//HttpJsonServerHandle.class
						//HttpJsonHandleBuilder.build(handlerClass,getInstance(handlerClass))
					}
				});
			future = b.bind(new InetSocketAddress(port));
		} catch(Exception e) {
			bossGroup.shutdownGracefully();
			workerGroup.shutdownGracefully();
		}
	}
	
	public void stop(){
		future.channel().closeFuture();
		bossGroup.shutdownGracefully();
		workerGroup.shutdownGracefully();
	}
	public static HttpJsonServer getInstance(Class<? extends ChannelHandler> handlerClass){
		synchronized (HttpJsonServer.class) {
			if(instance == null){
				instance = new HttpJsonServer();
				instance.handlerClass = handlerClass;
				Log.i(TAG,"HttpJsonServer created!");
			}
			return instance;
		}
	}
	
	synchronized public void addClient(ChannelHandlerContext ctx){
		if(clients.contains(ctx)){
			return;
		}
		clients.add(ctx);
		Log.i(TAG,"client add:"+ctx.channel().remoteAddress().toString()+" remain "+getClientsSize());
	}
	
	synchronized public void removeClient(ChannelHandlerContext ctx){
		if(clients.contains(ctx)){
			clients.remove(ctx);
			Log.i(TAG,"client remove:"+ctx.channel().remoteAddress().toString()+" remain "+getClientsSize());
		}
	}
	
	synchronized public int getClientsSize(){
		return clients.size();
	}
	
	
	synchronized public void broadcast(JsonResponse message){//new HttpJsonResponse(null, "broadcast")
		for(ChannelHandlerContext ctx : clients){
			if(ctx!=null && ctx.channel()!=null && ctx.channel().isActive()){
				Log.i(TAG,"JsonResponsebroadcast:"+message.getData());
				ChannelFuture future = ctx.writeAndFlush(new HttpJsonResponse(null, message));
			}
		}
	}
}
